/**
  ******************************************************************************
  * @file           : test_usb.cpp
  * @author         : wangyingjie
  * @brief          : None
  * @attention      : None
  * @date           : 2025/6/19
  ******************************************************************************
  */

#include "studio_macros.h"


#ifdef __cplusplus
extern "C"
{
#endif


#ifdef __cplusplus
}
#endif

#ifdef WIN32 // for windows
#   include <windows.h>
#   include <process.h>
#   include <stdio.h>
#   include <time.h>
#   include "controlcan.h"
#   pragma comment(lib, "controlcan.lib")
#   define msleep(ms)  Sleep(ms)
typedef HANDLE pthread_t;
#else // for linux
#   include <stdio.h>
#   include <stdlib.h>
#   include <string.h>
#   include <strings.h>
#   include <unistd.h>
#   include <sys/types.h>
#   include <sys/stat.h>
#   include <fcntl.h>
#   include <pthread.h>
#   include "controlcan.h"
#   define msleep(ms)  usleep((ms)*1000)
#   define min(a,b)  (((a) < (b)) ? (a) : (b))
#endif


#define MAX_CHANNELS  4
#define CHECK_POINT  200
#define RX_WAIT_TIME  100
#define RX_BUFF_SIZE  1000

unsigned gDevType = 0;
unsigned gDevIdx = 0;
unsigned gChMask = 0;
unsigned gBaud = 0;
unsigned gTxType = 0;
unsigned gTxSleep = 0;
unsigned gTxFrames = 0;
unsigned gTxCount = 0;

unsigned s2n(const char* s)
{
    unsigned l = strlen(s);
    unsigned v = 0;
    unsigned h = (l > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X'));
    unsigned char c;
    unsigned char t;
    if (!h)
        return atoi(s);
    if (l > 10)
        return 0;
    for (s += 2; c = *s; s++)
    {
        if (c >= 'A' && c <= 'F')
            c += 32;
        if (c >= '0' && c <= '9')
            t = c - '0';
        else if (c >= 'a' && c <= 'f')
            t = c - 'a' + 10;
        else
            return 0;
        v = (v << 4) | t;
    }
    return v;
}

void generate_frame(VCI_CAN_OBJ* can)
{
    memset(can, 0, sizeof(VCI_CAN_OBJ));
    can->SendType = gTxType;
    can->DataLen = 1 + (rand() % 8); // random data length: 1~8
    unsigned i;
    for (i = 0; i < can->DataLen; i++)
    {
        can->Data[i] = rand() & 0xff; // random data
        can->ID ^= can->Data[i];      // id: bit0~7, checksum of data0~N
    }
    can->ID |= ((unsigned)can->DataLen - 1) << 8; // id: bit8~bit10 = data_length-1
    can->ExternFlag = rand() % 2;                 // random frame format
    if (!can->ExternFlag)
        return;
    can->ID |= can->ID << 11; // id: bit11~bit21 == bit0~bit10
    can->ID |= can->ID << 11; // id: bit22~bit28 == bit0~bit7
}

int verify_frame(VCI_CAN_OBJ* can)
{
    if (can->DataLen > 8)
        return 0; // error: data length
    unsigned bcc = 0;
    unsigned i;
    for (i = 0; i < can->DataLen; i++)
        bcc ^= can->Data[i];
    if ((can->ID & 0xff) != bcc)
        return 0; // error: data checksum
    if (((can->ID >> 8) & 7) != (can->DataLen - 1))
        return 0; // error: data length
    if (!can->ExternFlag)
        return 1; // std-frame ok
    if (((can->ID >> 11) & 0x7ff) != (can->ID & 0x7ff))
        return 0; // error: frame id
    if (((can->ID >> 22) & 0x7f) != (can->ID & 0x7f))
        return 0; // error: frame id
    return 1;     // ext-frame ok
}

typedef struct
{
    unsigned channel; // channel index, 0~3
    unsigned stop;    // stop RX-thread
    unsigned total;   // total received
    unsigned error;   // error(s) detected
} RX_CTX;




#ifdef WIN32
unsigned __stdcall rx_thread(void *data)
#else
void* rx_thread(void* data)
#endif
{
    // 将传入的数据转换为 RX_CTX 类型，作为线程的上下文
    RX_CTX* ctx = (RX_CTX*)data;
    ctx->total = 0; // 初始化帧计数器

    // 定义缓冲区用于存储接收到的 CAN 帧
    VCI_CAN_OBJ can[RX_BUFF_SIZE];
    int cnt; // 存储当前接收的帧数量
    int i;

    unsigned check_point = 0;         // 用于记录打印进度的检查点
    while (!ctx->stop && !ctx->error) // 当未请求停止且无错误时循环运行
    {
        // 接收 CAN 帧数据，若无数据则继续循环
        cnt = VCI_Receive(gDevType, gDevIdx, ctx->channel, can, RX_BUFF_SIZE, RX_WAIT_TIME);
        if (!cnt)
            continue;


        if (ctx->error)
            break; // 如果有错误发生，退出主循环

        // 更新已接收的总帧数
        ctx->total += cnt;
        // 每达到一定帧数（由 CHECK_POINT 决定），打印一次接收统计
        if (ctx->total / CHECK_POINT >= check_point)
        {
            printf("CAN%d: %d frames received & verified\n", ctx->channel, ctx->total);
            check_point++; // 更新检查点
        }
        // 遍历所有接收到的帧，验证其完整性
        for (i = 0; i < cnt; i++)
        {
            // 打印接收到的 CAN 帧数据
            // printf("    CAN%d: 接收到帧 ID=0x%08X, 数据长度=%d, 数据内容:", ctx->channel, can[i].ID, can[i].DataLen);
            for (int j = 0; j < can[i].DataLen; j++)
            {
                printf(" 0x%02X", can[i].Data[j]);
            }
            printf("\n");

            if (verify_frame(&can[i])) // 如果帧验证失败
                continue;
            printf("CAN%d: verify_frame() failed\n", ctx->channel); // 打印错误信息
            ctx->error = 1;                                         // 设置错误标志
            break;                                                  // 跳出循环
        }
    }

    // 线程结束时输出终止信息，显示接收情况和是否检测到错误
    printf("CAN%d RX thread terminated, %d frames received & verified: %s\n",
           ctx->channel,
           ctx->total,
           ctx->error ? "error(s) detected" : "no error");

#ifdef WIN32
    _endthreadex(0); // Windows 下使用 _endthreadex 终止线程
    return 0;
#else
    pthread_exit(0); // Linux 下使用 pthread_exit 终止线程
#endif
}

int test()
{
    // ----- 初始化 & 启动 -------------------------------------------------
    // 初始化 CAN 配置参数
    VCI_INIT_CONFIG config;
    config.AccCode = 0;
    config.AccMask = 0xffffffff;
    config.Filter = 1;
    config.Mode = 0;
    config.Timing0 = gBaud & 0xff;
    config.Timing1 = gBaud >> 8;

    // 初始化并启动 CAN 通道
    int i, j;
    for (i = 0; i < MAX_CHANNELS; i++)
    {
        if ((gChMask & (1 << i)) == 0)
            continue;

        if (!VCI_InitCAN(gDevType, gDevIdx, i, &config))
        {
            printf("VCI_InitCAN(%d) 失败\n", i);
            return 0;
        }
        printf("VCI_InitCAN(%d) 成功\n", i);

        if (!VCI_StartCAN(gDevType, gDevIdx, i))
        {
            printf("VCI_StartCAN(%d) 失败\n", i);
            return 0;
        }
        printf("VCI_StartCAN(%d) 成功\n", i);
    }

    // ----- 接收超时测试 ----------------------------------------------
    // 测试接收超时以验证接收功能
    VCI_CAN_OBJ can;
    time_t tm1, tm2;
    for (i = 0; i < 3; i++)
    {
        time(&tm1);
        VCI_Receive(gDevType, gDevIdx, 0, &can, 1, (i + 1) * 1000/*毫秒*/);
        time(&tm2);
        printf("VCI_Receive %d 返回: 时间 ~= %ld 秒\n",i, tm2 - tm1);
    }

    // ----- 创建接收线程 --------------------------------------------
    // 为每个通道创建接收线程以并发接收数据
    RX_CTX rx_ctx[MAX_CHANNELS];
    pthread_t rx_threads[MAX_CHANNELS];
    for (i = 0; i < MAX_CHANNELS; i++)
    {
        if ((gChMask & (1 << i)) == 0)
            continue;

        rx_ctx[i].channel = i;
        rx_ctx[i].stop = 0;
        rx_ctx[i].total = 0;
        rx_ctx[i].error = 0;
#ifdef WIN32
        rx_threads[i] = (HANDLE)_beginthreadex(NULL, 0, rx_thread, &rx_ctx[i], 0, NULL);
#else
        pthread_create(&rx_threads[i], NULL, rx_thread, &rx_ctx[i]);
#endif
    }

    // ----- 等待 --------------------------------------------------------
    // 等待用户输入以开始传输
    printf("<ENTER> 开始发送: 每个通道 %d*%d 帧, 波特率: t0=0x%02x, t1=0x%02x...\n",
           gTxFrames,
           gTxCount,
           config.Timing0,
           config.Timing1);
    getchar();

    // ----- 开始发送 ---------------------------------------------------
    // 在所有通道上开始发送数据
    VCI_CAN_OBJ* buff = (VCI_CAN_OBJ*)malloc(sizeof(VCI_CAN_OBJ) * gTxFrames);
    time(&tm1);
    int err = 0;
    unsigned tx;
    for (tx = 0; !err && tx < gTxCount; tx++)
    {
        for (i = 0; i < MAX_CHANNELS; i++)
        {
            if ((gChMask & (1 << i)) == 0)
                continue;

            for (j = 0; j < gTxFrames; j++)
                generate_frame(&buff[j]);
            if (gTxFrames != VCI_Transmit(gDevType, gDevIdx, i, &buff[0], gTxFrames))
            {
                printf("CAN%d 发送失败: ID=%08x\n", i, can.ID);
                err = 1;
                break;
            }
        }
        if (gTxSleep)
            msleep(gTxSleep);
    }
    time(&tm2);
    free(buff);

    // ----- 停止发送 & 接收 -----------------------------------------------
    // 停止发送并等待接收线程完成
    msleep(1000);
    printf("发送停止, <ENTER> 终止接收线程...\n");
    getchar();

    for (i = 0; i < MAX_CHANNELS; i++)
    {
        if ((gChMask & (1 << i)) == 0)
            continue;

        rx_ctx[i].stop = 1;
#ifdef WIN32
        WaitForSingleObject(rx_threads[i], INFINITE);
        CloseHandle(rx_threads[i]);
#else
        pthread_join(rx_threads[i], NULL);
#endif
        if (rx_ctx[i].error)
            err = 1;
    }

    // ----- 报告 -------------------------------------------------------
    // 输出测试结果
    if (err)
    {
        printf("发现错误，测试失败\n");
        return 0;
    }

    printf("\n ***** 每个通道传输 %d 帧, 耗时 %ld 秒 *****\n",
           gTxFrames * gTxCount,
           tm2 - tm1);
    if (tm2 - tm1)
        printf("        性能: %ld 每个通道每秒帧数\n", gTxFrames * gTxCount / (tm2 - tm1));

    return 1;
}


// 添加新的接收打印函数
void receive_and_print()
{
    // 初始化CAN配置
    VCI_INIT_CONFIG config;
    config.AccCode = 0;
    config.AccMask = 0xffffffff;
    config.Filter = 1;
    config.Mode = 0;
    config.Timing0 = gBaud & 0xff;
    config.Timing1 = gBaud >> 8;

    // 初始化并启动CAN通道
    int i;
    for (i = 0; i < MAX_CHANNELS; i++) {
        if ((gChMask & (1 << i)) == 0)
            continue;

        if (!VCI_InitCAN(gDevType, gDevIdx, i, &config)) {
            printf("VCI_InitCAN(%d) failed\n", i);
            return;
        }
        printf("VCI_InitCAN(%d) succeeded\n", i);

        if (!VCI_StartCAN(gDevType, gDevIdx, i)) {
            printf("VCI_StartCAN(%d) failed\n", i);
            return;
        }
        printf("VCI_StartCAN(%d) succeeded\n", i);
    }

    // 选择要监控的通道
    const int monitor_channel = 1;  // 改为您要监控的通道

    printf("开始监控 CAN%d... (按 Ctrl+C 停止)\n", monitor_channel);

    while (1) {
        VCI_CAN_OBJ can[RX_BUFF_SIZE];
        int cnt = VCI_Receive(gDevType, gDevIdx, monitor_channel, can, RX_BUFF_SIZE, RX_WAIT_TIME);

        if (cnt > 0) {
            for (int i = 0; i < cnt; i++) {
                printf("CAN%d: ID=0x%08X %s Len=%d Data: ",
                       monitor_channel,
                       can[i].ID,
                       can[i].ExternFlag ? "EXT" : "STD",
                       can[i].DataLen);

                for (int k = 0; k < can[i].DataLen; k++) {
                    printf("%02X ", can[i].Data[k]);
                }
                printf("\n");
            }
        }
        // 添加详细调试信息
        else if (cnt == 0) {
            static int empty_count = 0;
            if (++empty_count % 10 == 0) {
                printf("CAN%d: 无数据 (尝试次数: %d)\n", monitor_channel, empty_count);
            }
        }
        else {
            printf("CAN%d: 接收错误 [错误代码: %d]\n", monitor_channel, cnt);
        }

        // 减少CPU占用
#ifdef WIN32
        Sleep(1);
#else
        usleep(1000);
#endif
    }
}


int main(int argc, char* argv[])
{
    // 初始化 CAN 设备配置 (可以根据需要自行设置 gDevType, gDevIdx 等)
    // gDevType = 4; // 例如：设备类型
    // gDevIdx = 0;  // 设备索引
    // gChMask = 0;  // 通道掩码
    // gBaud = 500000;  // 波特率
    // gTxType = 0;  // 发送类型
    // gTxSleep = 0;  // 发送延时
    // gTxFrames = 0; // 发送帧数
    // gTxCount = 0;  // 发送次数
    // printf("DevType=%d, DevIdx=%d, ChMask=0x%x, Baud=0x%04x, TxType=%d, TxSleep=%d, TxFrames=0x%08x(%d), TxCount=0x%08x(%d)\n", gDevType, gDevIdx, gChMask, gBaud, gTxType, gTxSleep, gTxFrames, gTxFrames, gTxCount, gTxCount);

    gDevType = s2n(argv[1]);
    gDevIdx = s2n(argv[2]);
    gChMask = s2n(argv[3]);
    gBaud = s2n(argv[4]);
    gTxType = s2n(argv[5]);
    gTxSleep = s2n(argv[6]);
    gTxFrames = s2n(argv[7]);
    gTxCount = s2n(argv[8]);
    printf("DevType=%d, DevIdx=%d, ChMask=0x%x, Baud=0x%04x, TxType=%d, TxSleep=%d, TxFrames=0x%08x(%d), TxCount=0x%08x(%d)\n",
           gDevType,
           gDevIdx,
           gChMask,
           gBaud,
           gTxType,
           gTxSleep,
           gTxFrames,
           gTxFrames,
           gTxCount,
           gTxCount);

    if (!VCI_OpenDevice(gDevType, gDevIdx, 0))
    {
        printf("VCI_OpenDevice failed\n");
        return 0;
    }
    printf("VCI_OpenDevice succeeded\n");


    test();
    // receive_and_print(); // 运行连续接收模式

    VCI_CloseDevice(gDevType, gDevIdx);
    printf("VCI_CloseDevice\n");
    return 0;

    return 0;
}

